// 出处： https://www.bilibili.com/read/cv22740260/?from=search&spm_id_from=333.337.0.0
#include <stdio.h>
#include <string.h>
#include <hs/hs.h>

// 回调函数
int eventHandler(unsigned int id, unsigned long long from,
    unsigned long long to, unsigned int flags, void *ctx)
{
    printf("Match found at end offset %llu\n", to);
    return 0;
}

// 在文件中匹配
int scanFile(const char *filename, hs_database_t *database, hs_scratch_t *scratch)
{
    FILE *file = fopen(filename, "r");
    if (!file) {
        fprintf(stderr, "Error opening file %s\n", filename);
        return 1;
    }

    // 获取文件大小
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // 为读取内容分配缓存
    char *buffer = (char *)malloc(size + 1);
    if (!buffer) {
        fprintf(stderr, "Error allocating memory\n");
        fclose(file);
        return 1;
    }

    // 读入文件内容
    fread(buffer, 1, size, file);
    fclose(file);
    buffer[size] = '\0';

    // 单模+块匹配
    hs_error_t err = hs_scan(database, buffer, size, 0, scratch,
        eventHandler, NULL);
    if (err != HS_SUCCESS) {
        fprintf(stderr, "Error scanning buffer\n");
        free(buffer);
        return 1;
    }

    free(buffer);
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc < 3) {
        fprintf(stderr, "Usage: %s pattern file1 [file2 ...]\n", argv[0]);
        return 1;
    }

    // 编译正则表达式
    hs_database_t *database = NULL;
    hs_compile_error_t *compile_err = NULL;
    hs_error_t err = hs_compile(argv[1], HS_FLAG_CASELESS, HS_MODE_BLOCK,
        NULL, &database, &compile_err);
    if (err != HS_SUCCESS) {
        fprintf(stderr, "Error compiling pattern: %s\n",
            compile_err->message);
        hs_free_compile_error(compile_err);
        return 1;
    }

    // 分配临时空间(指针必须初始化为NULL)
    hs_scratch_t *scratch = NULL;
    err = hs_alloc_scratch(database, &scratch);
    if (err != HS_SUCCESS) {
        fprintf(stderr, "Error allocating scratch space\n");
        hs_free_database(database);
        return 1;
    }

    // 处理命令行参数指定的每个文件
    for (int i = 2; i < argc; i++) {
        int ret = scanFile(argv[i], database, scratch);
        if (ret != 0) {
            hs_free_scratch(scratch);
            hs_free_database(database);
            return ret;
        }
    }

    // 释放空间
    hs_free_scratch(scratch);
    hs_free_database(database);

    return 0;
}
